#include <stdlib.h>
#include <stdio.h>
#include "../common.h"
#include "bitboard.h"
#include "bitboard_init.h"

bitboard rank_attacks[64][64];
bitboard file_attacks[64][64];
bitboard diaga1h8_attacks[64][64];
bitboard diaga8h1_attacks[64][64];

bitboard knight_attacks[64];
bitboard king_attacks[64];
bitboard pawn_attacks[2][64];
bitboard pawn_moves[2][64];

//board_t cur_board;
//bitboard pieces_rl90;
//bitboard pieces_rl45;
//bitboard pieces_rr45;

bitboard piece_setmask[64];
bitboard piece_setmask_rl90[64];
bitboard piece_setmask_rl45[64];
bitboard piece_setmask_rr45[64];

int coord_map_rl90[64];
int coord_map_rl45[64];
int coord_map_rr45[64];

const uint8_t left_attacks[8] = {
	0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
};

const uint8_t right_attacks[8] = {
	0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80, 0x00
};

board_t clear_board()
{
	board_t b;

	b.color[WHITE].pawns = 0x0LLU;
	b.color[WHITE].pawn_shadow = 0x0LLU;
	b.color[WHITE].rooks = 0x0LLU;
	b.color[WHITE].knights = 0x0LLU;
	b.color[WHITE].bishops = 0x0LLU;
	b.color[WHITE].queens = 0x0LLU;
	b.color[WHITE].king = 0x0LLU;

	b.color[BLACK].pawns = 0x0LLU;
	b.color[BLACK].pawn_shadow = 0x0LLU;
	b.color[BLACK].rooks = 0x0LLU;
	b.color[BLACK].knights = 0x0LLU;
	b.color[BLACK].bishops = 0x0LLU;
	b.color[BLACK].queens = 0x0LLU;
	b.color[BLACK].king = 0x0LLU;

	b.color[WHITE].all = 0x0LLU;

	b.color[BLACK].all = 0x0LLU;
	
	b.all_pieces = 0x0LLU;
	b.all_pieces_rl90 = 0x0LLU;
	b.all_pieces_rl45 = 0x0LLU;
	b.all_pieces_rr45 = 0x0LLU;

	b.qscastle[WHITE] = b.qscastle[BLACK] = 1;
	b.kscastle[WHITE] = b.kscastle[BLACK] = 1;

	return b;
}

board_t reset_board()
{
	board_t b;

	b.color[WHITE].pawns = 0xFF00LLU;
	b.color[WHITE].pawn_shadow = 0x0LLU;
	b.color[WHITE].rooks = 0x81LLU;
	b.color[WHITE].knights = 0x42LLU;
	b.color[WHITE].bishops = 0x24LLU;
	b.color[WHITE].queens = 0x8LLU;
	b.color[WHITE].king = 0x10LLU;

	b.color[BLACK].pawns = 0xFF000000000000LLU;
	b.color[BLACK].pawn_shadow = 0x0LLU;
	b.color[BLACK].rooks = 0x8100000000000000LLU;
	b.color[BLACK].knights = 0x4200000000000000LLU;
	b.color[BLACK].bishops = 0x2400000000000000LLU;
	b.color[BLACK].queens = 0x800000000000000LLU;
	b.color[BLACK].king = 0x1000000000000000LLU;

	b.color[WHITE].all = b.color[WHITE].pawns | b.color[WHITE].rooks | b.color[WHITE].knights |
	                     b.color[WHITE].bishops | b.color[WHITE].queens | b.color[WHITE].king;

	b.color[BLACK].all = b.color[BLACK].pawns | b.color[BLACK].rooks | b.color[BLACK].knights |
	                     b.color[BLACK].bishops | b.color[BLACK].queens | b.color[BLACK].king;

	b.all_pieces = b.color[WHITE].all | b.color[BLACK].all;
	b.all_pieces_rl90 = map_to_bitboard(b.all_pieces, piece_setmask_rl90);
	b.all_pieces_rl45 = map_to_bitboard(b.all_pieces, piece_setmask_rl45);
	b.all_pieces_rr45 = map_to_bitboard(b.all_pieces, piece_setmask_rr45);

	b.qscastle[WHITE] = b.qscastle[BLACK] = 1;
	b.kscastle[WHITE] = b.kscastle[BLACK] = 1;
	
/*
	b.color_rl90[WHITE] = map_to_bitboard_set(b.color[WHITE], piece_setmask_rl90);
	b.color_rl90[BLACK] = map_to_bitboard_set(b.color[BLACK], piece_setmask_rl90);

	b.color_rl45[WHITE] = map_to_bitboard_set(b.color[WHITE], piece_setmask_rl45);
	b.color_rl45[BLACK] = map_to_bitboard_set(b.color[BLACK], piece_setmask_rl45);
	
	b.color_rr45[WHITE] = map_to_bitboard_set(b.color[WHITE], piece_setmask_rr45);
	b.color_rr45[BLACK] = map_to_bitboard_set(b.color[BLACK], piece_setmask_rr45);
*/
	return b;
}


void generate_piece_setmasks()
{
	int x, y, i;

	for (y=0; y<8; y++) {
		for (x=0; x<8; x++) {
			i = x + (y<<3);
			piece_setmask[i] = BITSET(0, i); // ((bitboard) 1) << i;
			piece_setmask_rl90[i] = BITSET(0, (7-y) + (x<<3)); // ((bitboard) 1) << ((7-y) + (x<<3));
			coord_map_rl90[i] = (7-y) + (x<<3);
		}
	}

	for (x=0, y=0, i=0; i < 64; x++, y--, i++) {
		if (y == -1 && x < 8) {
			y = x;
			x = 0;
		} else if (x == 8) {
			x = y + 2;
			y = 7;
		}
		piece_setmask_rl45[(x + (y<<3))] = BITSET(0, i); // ((bitboard) 1) << i;
		coord_map_rl45[(x + (y<<3))] = i;
	}

	for (x=7, y=0, i=0; i < 64; x++, y++, i++) {
		if (x == 8 && y < 8) {
			x = 7-y;
			y = 0;
		} else if (y == 8) {
			y = (7-x) + 2;
			x = 0;
		}
		piece_setmask_rr45[(x + (y<<3))] = BITSET(0, i); // ((bitboard) 1) << i;
		coord_map_rr45[(x + (y<<3))] = i;
	}
}

bitboard map_to_bitboard(bitboard s, bitboard *setmask)
{
	int i;
	bitboard d = 0;

	for (i=0; i<64; i++) {
		if ((s >> i) & (bitboard)1) {
			d |= setmask[i];
		}
	}

	return d;
}

bitboard_set map_to_bitboard_set(bitboard_set s, bitboard *setmask)
{
	bitboard_set d;

	d.pawns = map_to_bitboard(s.pawns, setmask);
	d.rooks = map_to_bitboard(s.rooks, setmask);
	d.knights = map_to_bitboard(s.knights, setmask);
	d.bishops = map_to_bitboard(s.bishops, setmask);
	d.queens = map_to_bitboard(s.queens, setmask);
	d.king = map_to_bitboard(s.king, setmask);
	d.pawn_shadow = 0x0LLU;

	d.all = d.pawns | d.rooks | d.knights | d.bishops | d.queens | d.king;

	return d;
}

void generate_knight_attacks()
{
	int f2, r2, f, r;

	for(r=0; r<8; r++) {
		for (f=0; f<8; f++) {
			knight_attacks[(r<<3) + f] = 0;
			for (r2 = MAX(0,r-2); r2 <= MIN(7,r+2); r2++) {
				for (f2 = MAX(0,f-2); f2 <= MIN(7,f+2); f2++) {
					if (f2 == f && r2 == r)
						continue;

					if ( !( (ABS(f2-f) == 1 && ABS(r2-r) == 2) || (ABS(f2-f) == 2 && ABS(r2-r) == 1) ) )
						continue;
					
					knight_attacks[(r<<3) + f] = BITSET(knight_attacks[(r<<3) + f], (r2<<3) + f2);
				}
			}
		}
	}
}

void generate_king_attacks()
{
	int f2, r2, f, r;

	for(r=0; r<8; r++) {
		for (f=0; f<8; f++) {
			king_attacks[C2N(r,f)] = 0;
			for (r2 = MAX(0,r-1); r2 <= MIN(7,r+1); r2++) {
				for (f2 = MAX(0,f-1); f2 <= MIN(7,f+1); f2++) {
					if (f2 == f && r2 == r)
						continue;

					king_attacks[C2N(r,f)] = BITSET(king_attacks[C2N(r,f)], C2N(r2,f2));
				}
			}
		}
	}
}

void generate_pawn_attacks()
{
	int f, r;

	for(r=0; r<8; r++) {
		for (f=0; f<8; f++) {
			pawn_attacks[WHITE][C2N(r,f)] = 0;
			pawn_attacks[BLACK][C2N(r,f)] = 0;
			pawn_moves[WHITE][C2N(r,f)] = 0;
			pawn_moves[WHITE][C2N(r,f)] = 0;

			if (r < 7) {
				if (f > 0)
					pawn_attacks[WHITE][C2N(r,f)] = BITSET(pawn_attacks[WHITE][C2N(r,f)], C2N(r+1, f-1));
				if (f < 7)
					pawn_attacks[WHITE][C2N(r,f)] = BITSET(pawn_attacks[WHITE][C2N(r,f)], C2N(r+1, f+1));

				if (r == 1)
					pawn_moves[WHITE][C2N(r,f)] = BITSET(pawn_moves[WHITE][C2N(r,f)], C2N(r+2, f));

				pawn_moves[WHITE][C2N(r,f)] = BITSET(pawn_moves[WHITE][C2N(r,f)], C2N(r+1, f));
			}

			if (r > 0) {
				if (f > 0)
					pawn_attacks[BLACK][C2N(r,f)] = BITSET(pawn_attacks[BLACK][C2N(r,f)], C2N(r-1, f-1));
				if (f < 7)
					pawn_attacks[BLACK][C2N(r,f)] = BITSET(pawn_attacks[BLACK][C2N(r,f)], C2N(r-1, f+1));

				if (r == 6)
					pawn_moves[BLACK][C2N(r,f)] = BITSET(pawn_moves[BLACK][C2N(r,f)], C2N(r-2, f));

				pawn_moves[BLACK][C2N(r,f)] = BITSET(pawn_moves[BLACK][C2N(r,f)], C2N(r-1, f));
			}
		}
	}
}

void generate_rank_attacks()
{
	int r, f, i, blocking_square;
	uint8_t contents, blockers;
	bitboard attacks;

	for (f=0; f<8; f++) {
		for (i=0; i<64; i++) {
			contents = (i << 1);
			
			blockers = left_attacks[f] & contents;
			if (blockers) {
				blocking_square = first_one(blockers, sizeof(blockers));
				attacks = left_attacks[f] ^ left_attacks[blocking_square];
			} else {
				attacks = left_attacks[f];
			}

			blockers = right_attacks[f] & contents;
			if (blockers) {
				blocking_square = last_one(blockers);
				attacks |= right_attacks[f] ^ right_attacks[blocking_square];
			} else {
				attacks |= right_attacks[f];
			}

			for (r=0; r<8; r++) {
				rank_attacks[(r<<3) + f][i] = attacks << (r<<3);
			}
		}
	}
}

void generate_file_attacks()
{
	int r, f, i, j, blocking_square;
	uint8_t contents, blockers;
	bitboard attacks;

	for (r=0; r<8; r++) {
		for (i=0; i<64; i++) {
			contents = (i << 1);
			
			blockers = left_attacks[7-r] & contents;
			if (blockers) {
				blocking_square = first_one(blockers, sizeof(blockers));
				attacks = left_attacks[7-r] ^ left_attacks[blocking_square];
			} else {
				attacks = left_attacks[7-r];
			}

			blockers = right_attacks[7-r] & contents;
			if (blockers) {
				blocking_square = last_one(blockers);
				attacks |= right_attacks[7-r] ^ right_attacks[blocking_square];
			} else {
				attacks |= right_attacks[7-r];
			}

			for (f=0; f<8; f++) {
				// this transposes the row of bits in attack to a column of bits
				file_attacks[(r<<3) + f][i] = 0;
				for (j=0; j<8; j++) {
					// maybe there's a way to simplify this? i don't care this is "the most straightforward"
					// and it only ever runs once at the beginning, so speed isn't an issue...
					// anyway this is the reason for using rotated bitboards in the first place right?
					file_attacks[(r<<3) + f][i] |= ((attacks & (0x80>>j)) >> (7-j)) << (f + (j<<3));
				}
			}
		}
	}
}


void generate_diag_attacks()
{
	int r, f, i, x, y;//, len;
//	int8_t lmask;
	uint8_t contents;
	bitboard attacks;

	for (f=0; f<8; f++) {
		for (r=0; r<8; r++) {
			for (i=0; i<64; i++) {
				// generate array with all 8 bits of squares
				// for every diagonal, but only the lower bits
				// will matter if the diagonal has less than
				// 8 squares.

				contents = (i << 1);
			//	len = 8 - ABS(r-f);	// length of the diagonal
			//	if (len < 8) {
			//		// mask out the upper bits
			//		lmask = 0x80;
			//		lmask >>= (7-len);
			//		contents |= lmask;
			//	}

				attacks = 0;
				for (y=r+1, x=f+1; x<8 && y<8; x++, y++) {
					attacks = BITSET(attacks, x+(y<<3)); // |= ((bitboard) 1) << (x + (y<<3));
					if (contents & (1<<MIN(y,x)))
						break;
				}

				for (y=r-1, x=f-1; x>=0 && y>=0; x--, y--) {
					attacks = BITSET(attacks, x+(y<<3)); // |= ((bitboard) 1) << (x + (y<<3));
					if (contents & (1<<MIN(y,x)))
						break;
				}

				diaga1h8_attacks[(r<<3) + f][i] = attacks;


				attacks = 0;
				for (y=r+1, x=f-1; x>=0 && y<8; x--, y++) {
					attacks = BITSET(attacks, x+(y<<3)); // |= ((bitboard) 1) << (x + (y<<3));
					if (contents & (1<<MIN((7-y),x)))
						break;
				}

				for (y=r-1, x=f+1; x<8 && y>=0; x++, y--) {
					attacks = BITSET(attacks, x+(y<<3)); // |= ((bitboard) 1) << (x + (y<<3));
					if (contents & (1<<MIN((7-y),x)))
						break;
				}

				diaga8h1_attacks[(r<<3) + f][i] = attacks;


			}
		}
	}
}

void bitboard_system_init()
{
	generate_piece_setmasks();
	generate_rank_attacks();
	generate_file_attacks();
	generate_diag_attacks();
	generate_knight_attacks();
	generate_king_attacks();
	generate_pawn_attacks();
}

